﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using AirTunesStreamer.Properties;
using System.Windows.Input;

namespace AirTunesStreamer
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        private RAOPFinder _finder;
        private Streamer _streamer;
        private bool _started;
        private HashSet<string> tasks = new HashSet<string>();
        private bool _selecting;

        public string Host
        {
            get { return textBoxHost.Text; }
            set 
            {
                _selecting = true;
                textBoxHost.Text = value;
                _selecting = false;
            }
        }

        public MainWindow()
        {
            AppDomain.CurrentDomain.UnhandledException += delegate(object sender, UnhandledExceptionEventArgs e) { 
                ShowError(e.ExceptionObject.ToString()); 
            };

            _streamer = new Streamer();
            _streamer.OnError += streamer_OnError;
            InitializeComponent();
            sliderVolume.Value = Settings.Default.Volume;
            Host = Settings.Default.Host;
            try
            {
                _finder = new RAOPFinder(Found, Resolved, Lost);
            }
            catch (Exception e)
            {
                ShowError("Bonjour is not installed. Airport discovery is disabled.");
            }
            UpdateButtonState();
        }

        private void ShowError(string msg)
        {
            //todo: if minimized the show baloon else show messagebox
            if (!Dispatcher.CheckAccess())
            {
                Dispatcher.Invoke(new Action<string>(ShowError), msg);
            }
            else
            {
                MessageBox.Show(msg);
            }
        }

        private void ShowInfo(string msg)
        {
            //todo: show notification baloon
        }

        void streamer_OnError(Exception e)
        {
            try
            {
                Invoke(Stop);
            }
            catch (Exception e1) 
            {
                Log(e1.ToString());
            }
            
            ShowError("Transmission was interrupted. " + e.Message);
            Log(e.ToString());
        }

        private void Stop()
        {
            try
            {
                _streamer.Stop();
            }
            finally
            {
                _started = false;
                UpdateButtonState();
            }
        }

        private void Start()
        {
            _streamer.Start(Host);
            _started = true;
            UpdateButtonState();
        }

        private void Invoke(Action action)
        {
            if (!Dispatcher.CheckAccess())
            {
                Dispatcher.Invoke(action);
            }
            else
            {
                Cursor oldCursor = Cursor;
                Cursor = Cursors.Wait;
                try
                {
                    action();
                }
                finally
                {
                    Cursor = oldCursor;
                }
            }
        }

        private void Log(string msg)
        {
            Console.WriteLine(msg);
        }

        private void Found(string name)
        {
            listBoxServices.Items.Add(name);
        }

        private void Lost(string name)
        {
            listBoxServices.Items.Remove(name);
        }

        private void Resolved(string service, string host)
        {
            if (listBoxServices.SelectedItem as string == service)
            {
                Host = host;
                buttonStartStop.IsEnabled = true;
            }
            tasks.Remove(service);
        }

        private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            Settings.Default.Volume = sliderVolume.Value;
            Settings.Default.Host = Host;
            Settings.Default.Save();

            _streamer.Dispose();
            if (_finder != null)
                _finder.Dispose();
        }

        private void listBoxServices_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (e.AddedItems.Count > 0)
            {
                string name = e.AddedItems[0] as string;
                
                Host = null;
                
                tasks.Add(name);
                _finder.Resolve(name);
            }
        }

        private void buttonStartStop_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                if (_started)
                {
                    Invoke(Stop);
                }
                else
                {
                    Invoke(Start);
                }
            }
            catch (Exception ex)
            {
                ShowError(ex.Message);
                _streamer.Stop();
                return;
            }
        }

        private void UpdateButtonState()
        {
            buttonStartStop.Content = _started ? "Stop" : "Start";
            if (!_started)
                buttonStartStop.IsEnabled = Host.Contains('.');
        }

        private void textBoxHost_TextChanged(object sender, TextChangedEventArgs e)
        {
            string s = listBoxServices.SelectedItem as string;
            if (!_selecting && !tasks.Contains(s) && textBoxHost.Text != s)
                listBoxServices.SelectedItem = null;
            UpdateButtonState();
        }

        private void sliderVolume_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            _streamer.SetVolume(sliderVolume.Value);
        }

    }
}
